# 31. 模块的进阶


# from导入

from可以在模块中导入指定的模块属性(对象)、也就是说把指定的模块的对象导入到当前空间来

使用格式:

from 模块名 import 导入的对象名

# 模块文件

print("这是一个自定义模块中的打印输出")

name = "这是一个自定义模块中的变量"

def inso():
    print("这是一个自定义模块中的函数")

# 导入单对象

from so_msg import inso
inso()

执行结果:
这是一个自定义模块中的打印输出
这是一个自定义模块中的函数

# 导入多对象

from so_msg import inso,name
inso()
print(name)

执行结果:
这是一个自定义模块中的打印输出
这是一个自定义模块中的函数
这是一个自定义模块中的变量

# 导入单对象时更名

from so_msg import inso as s
s()

执行结果:
这是一个自定义模块中的打印输出
这是一个自定义模块中的函数

# 导入多对象时更名

from so_msg import inso as s, name as nm
s()
print(nm)

执行结果:
这是一个自定义模块中的打印输出
这是一个自定义模块中的函数
这是一个自定义模块中的变量

# * 符号

*符号:代表导入模块中的所有对象

from so_msg import *
inso()
print(name)

执行结果:
这是一个自定义模块中的打印输出
这是一个自定义模块中的函数
这是一个自定义模块中的变量

# __add__的使用

add:用于模块文件,让模块文件限制 用 * 符号导入模块有多少变量,只是限制 * 符号

注意:add 必须要用 中括号 [ ] ,允许用 * 的对象要用字符串类型输入

模块文件

__all__ = ['inso']

print("这是一个自定义模块中的打印输出")

name = "这是一个自定义模块中的变量"

def inso():
    print("这是一个自定义模块中的函数")

py执行文件

from so_msg import *
inso()
print(name)

执行结果:
这是一个自定义模块中的函数
NameError: name 'name' is not defined

# 把模块当成脚本执行

python执行有二种模式:脚本执行跟模块执行

模块执行

  1. import
  2. import
  3. from ... import

脚本执行

  1. 在pycharm中右键执行
  2. 在linux中,python 文件名 执行
  3. 等等

# name :判断是否在当前文件中执行

先来看看__name__的执行效果

在模块当前执行

name = "这是一个自定义模块中的变量1"

def inso():
    print("这是一个自定义模块中的函数")

print(__name__)

执行结果:
__main__

在py文件中导入模块执行

from so_msg import *

执行结果:
so_msg

到这里有没有发现什么规律,__name__函数在模块的脚本执行返回的是"main",在另一个文件以模块导入的方式执行返回的是文件名"so_msg",所以可以用这个__name__来判断是否是当前文件执行


# 让模块文件有些代码只能在当地执行

使用__name__函数来进行判断,如果是当前文件就执行,如果不是当前文件就不执行

##模块文件
if __name__ == "__main__":print("你好")
name = "这是一个自定义模块中的变量1"
def inso():
    print("这是一个自定义模块中的函数")
    
##py文件
from so_msg import *
inso()
print(name)

执行结果:
这是一个自定义模块中的函数
这是一个自定义模块中的变量1

这样子就可以让模块的一些执行输出,不会在导入模块的时候自动执行,也可以让模块文件能够脚本化,能当成模块文件,也能当成脚本文件

还可以这样理解:在编写py文件的时候,所有不在函数和类中封装的内容都应该写在,if name == 'main':这里


# 反射在模块中的补充

在反射中,是不是说过sys模块中的modules["main"],获取当前文件的内存地址

在这里在补充一点,sys模块中的modules["main"],无论是在模块中执行还是用于py文件执行都是显示当前执行空间的内存地址

## 模块文件执行
import sys
print(sys.modules["__main__"])

执行结果:
<module '__main__' from 'D:/python/PyCharm资源/one/模块/so_msg.py'>

## py文件执行
from so_msg import *

执行结果:
<module '__main__' from 'D:/python/PyCharm资源/one/模块/模块初始.py'>

有没有发现什么,为什么sys模块中的modules["main"],是在模块文件中的,在py文件中执行的结果竟然是py文件的内存地址。

这里强调一点,sys模块中的modules["main"],得到的结果来源于当前那个空间执行的py文件内存地址

那这样不是模块中不能使用反射了吗,因为反射的时候可以会用到sys模块中的modules["main"],来获取当前文件的内存地址,如果自己这样用,那么这个模块文件导入到另一个文件的时候,那反射还能正常用吗!!!


# 使用另一个方式

把sys模块中的modules["main"]中的__main__ 换成自己的__name__函数,因为modules出的值都会出于这文件关联的所有文件内存地址,__name__就是代表当前空间执行的文件的内存,其他文件都是以 {文件名:内存地址} 这种方式存储的,这样子使用__name__无论是在模块当前中执行也可以获取模块的内存地址,还是在Py文件导入模块的时候执行也可以获取模块的内存地址,这样子模块的反射就可以正常使用了

##模块文件
import sys
name = "这是一个自定义模块中的变量1"
def inso():
    print("这是一个自定义模块中的函数")
getattr(sys.modules[__name__],"inso")()
print(getattr(sys.modules[__name__],"name"))

##py文件
from so_msg import *

执行结果:
这是一个自定义模块中的函数
这是一个自定义模块中的变量1

​ 这样反射在模块文件中也可以正常使用,这样子也可以让模块文件当成脚本文件执行


# pyc编译文件

当python执行import 模块名 时,就会在当前目录下的__pycache__中生成一个编译好的文件

如果__pycache__不存在,会默认创建的

为什么要编译生成呢,python执行时,其实也是在编译文件,只是编译存放在内存

但是模块呢,在Python认为模块是一个常用又不常修改的py文件,那么他就有一个机制,执行的时候会把编译文件存放在指定的目录中,等下次执行调用模块的时候就可以直接调用这个文件,就不用重新编译模块文件了

这样到以后做比较大的项目的时候,会有效的提升代码的执行顺序

那么如果模块文件被修改了呢,放心,Python会自动的重新生成编译文件覆盖过去

注意:提高的代码效率是指代码中模块的导入效率,并不是代码的其它效率


# 重新加载模块

在py文件执行的时候,修改了已经导入的模块内容,是不生效的

## 模块文件
def inso():
    print("这是一个自定义模块中的函数1")
    
## Py文件
import time
from so_msg import *
inso()
time.sleep(5)
inso()

执行结果:
这是一个自定义模块中的函数
这是一个自定义模块中的函数

## 当Py文件执行到time.sleep(5)的时候,就会停留5秒后在执行,这时候去修改模块文件的代码,可以看看等5秒后执行的是不是修改后的内容

那为什么不会生效呢,因为python执行时会把所有的包含模块的编译放在内存上,你修改的只是硬盘中的模块代码,又不是内存中编译好的内容,怎么可能会生效


# 使用importlib模块强制更新模块

这个超级超级不建议使用

## 模块文件
def inso():
    print("这是一个自定义模块中的函数")
    
## py文件
import time
import importlib
import so_msg
so_msg.inso()
time.sleep(5)
importlib.reload(so_msg)
so_msg.inso()

执行结果:
这是一个自定义模块中的函数
这是一个自定义模块中的函数1

# 模块的循环导入

模块的循环导入,只有一个原则:不允许使用

python中模块是无法做到循环相互导入的。

# 模块文件:so_msg
import msg
def inso():
    print("这是一个自定义模块中的函数1")
    
# Py执行文件:msg
import so_msg
so_msg.inso()

执行结果:
AttributeError: module 'so_msg' has no attribute 'inso'

为什么会提示找不到so_msg,因为so_msg文件执行是导入so_msg模块,从而执行so_msg模块文件,so_msg模块文件又从头开始执行,第一行代码就是导入msg模块,那么又会回来msg文件中从头开始执行,这时import msg,还会不会执行!!!

答案是不会,因为这模块文件已经编译导入内存中,Python会自动检测,如果发现导入过一次就不会在执行导入操作,那这样子,就到了执行代码环节

为什么会找不到,执行so_msg.inso(),那么在so_msg模块中的inso()函数会执行到没,没执行到那怎么找得到

所以在这建议永远不要写循环的模块架构